package com.och.mrcp.core.sip.transaction;


import com.och.mrcp.core.sip.model.SipRequest;
import com.och.mrcp.core.sip.model.SipResponse;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

@Data
@Slf4j
public class SipTransaction {
    
    public enum State {
        TRYING,           // 100 Trying sent
        PROCEEDING,       // 1xx response received
        COMPLETED,        // Final response received
        TERMINATED,       // Transaction terminated
        TIMEOUT           // Transaction timed out
    }
    
    public enum Type {
        INVITE_CLIENT,    // INVITE client transaction
        INVITE_SERVER,    // INVITE server transaction
        NON_INVITE_CLIENT, // Non-INVITE client transaction
        NON_INVITE_SERVER  // Non-INVITE server transaction
    }
    
    private String transactionId;
    private Type type;
    private State state;
    private SipRequest originalRequest;
    private SipResponse provisionalResponse;
    private SipResponse finalResponse;
    private long startTime;
    private long timeout;
    private AtomicInteger retryCount = new AtomicInteger(0);
    private int maxRetries = 10;
    
    private CompletableFuture<SipResponse> responseFuture;
    
    public SipTransaction(SipRequest request, Type type) {
        this.transactionId = generateTransactionId(request);
        this.type = type;
        this.state = State.TRYING;
        this.originalRequest = request;
        this.startTime = System.currentTimeMillis();
        this.timeout = 32000; // 32 seconds default timeout
        this.responseFuture = new CompletableFuture<>();
    }
    
    private String generateTransactionId(SipRequest request) {
        return request.getCallId() + "-" + request.getCseq() + "-" + request.getMethod();
    }
    
    public void updateState(State newState) {
        log.debug("Transaction {} state change: {} -> {}", transactionId, state, newState);
        this.state = newState;
    }
    
    public void setProvisionalResponse(SipResponse response) {
        this.provisionalResponse = response;
        if (state == State.TRYING) {
            updateState(State.PROCEEDING);
        }
    }
    
    public void setFinalResponse(SipResponse response) {
        this.finalResponse = response;
        updateState(State.COMPLETED);
        responseFuture.complete(response);
    }
    
    public void timeout() {
        updateState(State.TIMEOUT);
        responseFuture.completeExceptionally(new RuntimeException("Transaction timeout"));
    }
    
    public void terminate() {
        updateState(State.TERMINATED);
        if (!responseFuture.isDone()) {
            responseFuture.cancel(true);
        }
    }
    
    public boolean isExpired() {
        return System.currentTimeMillis() - startTime > timeout;
    }
    
    public boolean canRetry() {
        return retryCount.get() < maxRetries && state != State.COMPLETED && state != State.TERMINATED;
    }
    
    public void incrementRetryCount() {
        retryCount.incrementAndGet();
    }
    
    public CompletableFuture<SipResponse> getResponseFuture() {
        return responseFuture;
    }
    
    public CompletableFuture<SipResponse> getResponseFuture(long timeout, TimeUnit unit) {
        return responseFuture.orTimeout(timeout, unit);
    }
    
    @Override
    public String toString() {
        return String.format("SipTransaction{id=%s, type=%s, state=%s, method=%s, callId=%s}",
                transactionId, type, state, 
                originalRequest != null ? originalRequest.getMethod() : "null",
                originalRequest != null ? originalRequest.getCallId() : "null");
    }
}
